package com.example.spring.framework.webmvc.servlet;

import com.example.spring.framework.context.ApplicationContext;
import com.example.spring.framework.annotation.Controller;
import com.example.spring.framework.annotation.RequestMapping;
import com.example.spring.framework.webmvc.filter.FilterChain;

import javax.servlet.ServletConfig;
import javax.servlet.ServletException;
import javax.servlet.http.HttpServlet;
import javax.servlet.http.HttpServletRequest;
import javax.servlet.http.HttpServletResponse;
import java.io.File;
import java.io.IOException;
import java.lang.reflect.Method;
import java.util.*;
import java.util.regex.Matcher;
import java.util.regex.Pattern;

/**
 * @Classname DispatcherServlet
 * @Date 2019/5/4 14:03
 * @Author admin
 * @Description TODO
 */

public class DispatcherServlet extends HttpServlet {

    private final String CONTEXT_CONFIG_LOCATION = "contextConfigLocation";

    private ApplicationContext context;

    private List<HandlerMapping> handlerMappings = new ArrayList<HandlerMapping>();

    private Map<HandlerMapping, HandlerAdapter> handlerAdapters = new HashMap<HandlerMapping, HandlerAdapter>();

    private List<ViewResolver> viewResolvers = new ArrayList<ViewResolver>();

    @Override
    protected void doGet(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        this.doPost(req, resp);
    }

    @Override
    protected void doPost(HttpServletRequest req, HttpServletResponse resp) throws ServletException, IOException {
        try {
            this.doDispatch(req, resp);
        } catch (Exception e) {
            resp.getWriter().write("500 Exception,Details:\r\n" + Arrays.toString(e.getStackTrace()).replaceAll("\\[|\\]", "").replaceAll(",\\s", "\r\n"));
            e.printStackTrace();
        }
    }

    @Override
    public void init(ServletConfig config) throws ServletException {
        //1、初始化ApplicationContext
        context = new ApplicationContext(config.getInitParameter(CONTEXT_CONFIG_LOCATION));
        //2、初始化Spring MVC 九大组件
        initStrategies(context);
    }

    private void doDispatch(HttpServletRequest req, HttpServletResponse resp) throws Exception {
        //1、通过从request中拿到URL，去匹配一个HandlerMapping
        HandlerMapping handlerMapping = getHandlerMapping(req);

        if (null == handlerMapping) {
            processDispatchResult(req, resp, new ModelAndView("404"));
            return;
        }

        //执行过滤器调用链
        new FilterChain().doFilter(handlerMapping,req, resp);

        //2、准备调用前的参数
        HandlerAdapter handlerAdapter = getHandlerAdapter(handlerMapping);

        //3、真正的调用方法,返回ModelAndView存储了要穿页面上值，和页面模板的名称
        ModelAndView mv = handlerAdapter.handle(req, resp, handlerMapping);

        //4、输出
        processDispatchResult(req, resp, mv);

    }

    private HandlerMapping getHandlerMapping(HttpServletRequest req) throws Exception {
        if (this.handlerMappings.isEmpty()) {
            return null;
        }

        String url = req.getRequestURI();
        String contextPath = req.getContextPath();

        url = url.replace(contextPath, "").replaceAll("/+", "/");

        for (HandlerMapping handleMapping : handlerMappings) {
            try {
                Matcher matcher = handleMapping.getPattern().matcher(url);
                if (!matcher.matches()) {
                    continue;
                }

                return handleMapping;
            } catch (Exception e) {
                throw e;
            }
        }
        return null;
    }

    private HandlerAdapter getHandlerAdapter(HandlerMapping handlerMapping) {
        if (this.handlerAdapters.isEmpty()) {
            return null;
        }

        HandlerAdapter ha = this.handlerAdapters.get(handlerMapping);

        if (ha.supports(handlerMapping)) {
            return ha;
        }

        return null;
    }

    /**
     * 把给我的ModleAndView变成一个HTML、OutPutStream、json、freemarker、velocity
     *
     * @param req
     * @param resp
     * @param mv
     * @throws Exception
     */
    private void processDispatchResult(HttpServletRequest req, HttpServletResponse resp, ModelAndView mv) throws Exception {
        if (null == mv) {
            return;
        }

        if (this.viewResolvers.isEmpty()) {
            return;
        }

        for (ViewResolver viewResolver : this.viewResolvers) {
            View view = viewResolver.resolveViewName(mv.getViewName(), null);
            view.render(mv.getModel(), req, resp);
            return;
        }


    }

    /**
     * 初始化策略
     *
     * @param context
     */
    protected void initStrategies(ApplicationContext context) {
        //多文件上传的组件
        initMultipartResolver(context);
        //初始化本地语言环境
        initLocaleResolver(context);
        //初始化模板处理器
        initThemeResolver(context);


        //handlerMapping，必须实现
        initHandlerMappings(context);
        //初始化参数适配器，必须实现
        initHandlerAdapters(context);
        //初始化异常拦截器
        initHandlerExceptionResolvers(context);
        //初始化视图预处理器
        initRequestToViewNameTranslator(context);


        //初始化视图转换器，必须实现
        initViewResolvers(context);
        //参数缓存器
        initFlashMapManager(context);
    }

    private void initMultipartResolver(ApplicationContext context) {
    }

    private void initLocaleResolver(ApplicationContext context) {
    }

    private void initThemeResolver(ApplicationContext context) {
    }

    private void initHandlerMappings(ApplicationContext context) {
        String[] beanNames = context.getBeanDefinitionNames();

        try {
            for (String beanName : beanNames) {
                Object controller = context.getBean(beanName);

                Class<?> clazz = controller.getClass();

                System.out.println(clazz);

                if (!clazz.isAnnotationPresent(Controller.class)) {
                    continue;
                }

                String baseUrl = "";
                //获取Controller的url配置
                if (clazz.isAnnotationPresent(RequestMapping.class)) {
                    RequestMapping requestMapping = clazz.getAnnotation(RequestMapping.class);
                    baseUrl = requestMapping.value();
                }

                //获取Method的url配置
                Method[] methods = clazz.getMethods();
                for (Method method : methods) {

                    //没有加RequestMapping注解的直接忽略
                    if (!method.isAnnotationPresent(RequestMapping.class)) {
                        continue;
                    }

                    //映射URL
                    RequestMapping requestMapping = method.getAnnotation(RequestMapping.class);

                    String regex = ("/" + baseUrl + requestMapping.value().replaceAll("\\*", ".*")).replaceAll("/+", "/");
                    Pattern pattern = Pattern.compile(regex);

                    this.handlerMappings.add(new HandlerMapping(controller, method, pattern));

                    System.out.print("Mapped " + regex + "," + method);
                }
            }

        } catch (Exception e) {
            e.printStackTrace();
        } finally {
            System.out.println("handlerMappings 初始化完毕......");
        }

    }

    /**
     * 把一个request请求变成一个handler，参数都是字符串的，自动配到handler中的形参
     * 可想而知，他要拿到HandlerMapping才能干活
     * 就意味着，有几个HandlerMapping就有几个HandlerAdapter
     *
     * @param context
     */
    private void initHandlerAdapters(ApplicationContext context) {
        for (HandlerMapping handlerMapping : this.handlerMappings) {
            this.handlerAdapters.put(handlerMapping, new HandlerAdapter());
        }
    }

    private void initHandlerExceptionResolvers(ApplicationContext context) {
    }

    private void initRequestToViewNameTranslator(ApplicationContext context) {
    }

    private void initViewResolvers(ApplicationContext context) {
        //拿到模板的存放目录
        String templateRoot = context.getConfig().getProperty("templateRoot");
        String templateRootPath = this.getClass().getClassLoader().getResource(templateRoot).getFile();

        File templateRootDir = new File(templateRootPath);
        String[] templates = templateRootDir.list();

        for (String str : templates) {
            //这里主要是为了兼容多模板，所有模仿Spring用List保存,为了仿真，所有还是搞了个List
            this.viewResolvers.add(new ViewResolver(templateRoot));
        }
    }

//    private void initViewResolvers(String path){
//        File templateRootDir = new File(path);
//        String[] templates = templateRootDir.list();
//
//        for (String str : templates) {
//            String tempPath = path + str;
//            File tempDir = new File(path);
//            if(tempDir.isDirectory()){
//                this.initViewResolvers(tempPath);
//            } else {
//                this.viewResolvers.add(new ViewResolver(templateRoot));
//            }
//        }
//    }

    private void initFlashMapManager(ApplicationContext context) {
    }


}
